package com.gitee.wsl.mathematics.geometry.d2.contour.segment.ext

import com.gitee.wsl.mathematics.geometry.d2.base.YPolarity
import com.gitee.wsl.mathematics.geometry.d2.contour.segment.Segment2D
import com.gitee.wsl.mathematics.geometry.d2.line.Line
import com.gitee.wsl.mathematics.geometry.d2.line.ext.getIntersectionPoint
import com.gitee.wsl.mathematics.vector.ext.distanceTo
import com.gitee.wsl.mathematics.vector.ext.dot
import com.gitee.wsl.mathematics.vector.ext.minus
import com.gitee.wsl.mathematics.vector.ext.projectOn
import com.gitee.wsl.mathematics.vector.ext.times
import com.gitee.wsl.mathematics.vector.vec2.Vec2f
import kotlin.math.sign

fun Segment2D.scale(scale: Double, polarity: YPolarity) = scale(polarity) { scale }

fun Segment2D.scale(polarity: YPolarity, scale: (Double) -> Double): Segment2D {
    if (control.size == 1) {
        return cubic.scale(polarity, scale)
    }

    val newStart = start + normal(0.0f, polarity) * scale(0.0)
    val newEnd = end + normal(1.0f, polarity) * scale(1.0)

    val a = Line(newStart, start)
    val b = Line(newEnd, end)

    val o = a.getIntersectionPoint(b)
    //val o = intersection(a, b, 1E7)

    if (o != null) {
        val newControls = control.mapIndexed { index, it ->
            val d = it - o
            val rc = scale((index + 1.0) / 3.0)
            val s = normal(0.0f, polarity).dot(d).sign
            val nd = d.normalized * s
            it + rc * nd
        }
        return copy(newStart, newControls, newEnd)
    } else {
        val newControls = control.mapIndexed { index, it ->
            val rc = scale((index + 1.0) / 3.0)
            it + rc * normal((index + 1.0f), polarity)
        }
        return copy(newStart, newControls, newEnd)
    }
}


/**
 * Scale tangents such that tangent lines do not overlap
 */
fun Segment2D.scaleTangents(axis: Vec2f = Vec2f.UNIT_X): Segment2D {
    if (linear) {
        return this
    } else {
        val c = this.cubic
        val width = end.distanceTo(start)

        val d = c.end - c.start
        val cd0 = (c.control[0] - c.start).projectOn(axis)
        val cd0a = cd0.dot(axis)
        val cd1 = (c.control[1] - c.end).projectOn(-axis)
        val cd1a = cd1.dot(-axis)
        val handleWidth = cd0.length + cd1.length

        val r = width / handleWidth
        val c0 = (if (handleWidth > width) (c.control[0] - c.start) * r + c.start else c.control[0]).let {
            if (cd0a <= 0.0) {
                (it - c.start).projectOn((axis).perpendicular) + c.start
            } else {
                it
            }
        }
        val c1 = (if (handleWidth > width) (c.control[1] - c.end) * r + c.end else c.control[1]).let {
            if (cd1a <= 0.0) {
                (it - c.end).projectOn((-axis).perpendicular) + c.end
            } else {
                it
            }
        }
        return copy(control = listOf(c0, c1))
    }
}